﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using IOP.SgrA;
using IOP.SgrA.SilkNet.Vulkan;
using System.Text.Json;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Drawing;
using Microsoft.Extensions.Logging;
using Silk.NET.Vulkan;

namespace VkSample82
{
    [ModulePriority(ModulePriority.RenderGroup)]
    public class EarthPipelineModule : VulkanModule, IVulkanRenderWapperModule
    {
        private readonly ILogger<EarthPipelineModule> _Logger;

        public EarthPipelineModule(ILogger<EarthPipelineModule> logger)
        {
            _Logger = logger;
        }

        public VulkanRenderWapper RenderWapper { get; set; }

        protected override async Task Load(VulkanGraphicsManager graphicsManager)
        {
            try
            {
                graphicsManager.TryGetCamera("main", out Camera camera);
                var lDevice = graphicsManager.VulkanDevice;
                var basePath = AppContext.BaseDirectory;
                var shaders = Path.Combine(basePath, "Assets", "Shaders");
                VulkanShaderInfo[] infos = new VulkanSPVShaderInfo[]
                {
                    new VulkanSPVShaderInfo(ShaderTypes.VertexShader, "earthVert", new FileInfo(Path.Combine(shaders, "earthVert.spv"))),
                    new VulkanSPVShaderInfo(ShaderTypes.FragmentShader, "earthFrag", new FileInfo(Path.Combine(shaders, "earthFrag.spv"))),
                };
                VulkanShaderInfo[] cloudInfos = new VulkanSPVShaderInfo[]
                {
                    new VulkanSPVShaderInfo(ShaderTypes.VertexShader, "cloudVert", new FileInfo(Path.Combine(shaders, "cloudVert.spv"))),
                    new VulkanSPVShaderInfo(ShaderTypes.FragmentShader, "cloudFrag", new FileInfo(Path.Combine(shaders, "cloudFrag.spv"))),
                };
                await Task.WhenAll(infos[0].LoadShader(), infos[1].LoadShader(), cloudInfos[0].LoadShader(), cloudInfos[1].LoadShader());
                JsonDocument json = await JsonDocument.ParseAsync(File.OpenRead(Path.Combine(shaders, "pipeline.json")));

                var pipelineCache = lDevice.PipelineCache;
                graphicsManager.TryGetRenderGroup("MainRenderGroup", out IRenderGroup group);
                var mainGroup = group as PrimaryVulkanRenderGroup;

                var earthPipelineJson = json.RootElement.GetProperty("EarthPipeline").ToString();
                var earthJson = json.RootElement.GetProperty("EarthPipelineLayoutCreateOption").ToString();
                var earthPipeline = graphicsManager.CreateEmptyPipeline($"Earth")
                    .BuildPipelineLayoutFromJson(json.RootElement.GetProperty("EearthDescriptorSetLayoutCreateOptions").GetRawText(), earthJson)
                    .BuildUniformBuffer(0, 0, sizeof(float) * 20, SharingMode.Exclusive)
                    .BuildGraphicsPipelineFromJson(mainGroup.RenderPass, pipelineCache, infos, earthPipelineJson);
                var sCommand = lDevice.CreateCommandPool((device, option) =>
                {
                    option.Flags = CommandPoolCreateFlags.CommandPoolCreateResetCommandBufferBit;
                    option.QueueFamilyIndex = device.WorkQueues[0].FamilyIndex;
                }).CreateSecondaryCommandBuffer();
                var secondary = graphicsManager.CreateSecondaryVulkanRenderGroup("EarthGroup", earthPipeline)
                    .Binding(mainGroup, sCommand, new VulkanSemaphore[0], new VulkanFence[0]);
                secondary.SetCamera(camera);
                secondary.CreateGroupRenderingAction((builder) => builder.UseMiddleware<BallRenderingMiddleware>());
                secondary.CreateItemRenderingAction((builder) => builder.UseMiddleware<BallItemMiddleware>());

                var cloudJson = json.RootElement.GetProperty("CloudPipelineLayoutCreateOption").ToString();
                var cloudPipelineJson = json.RootElement.GetProperty("CloudPipeline").ToString();
                var cloudPipeline = graphicsManager.CreateEmptyPipeline("Cloud")
                    .BuildPipelineLayoutFromJson(json.RootElement.GetProperty("MoonDescriptorSetLayoutCreateOptions").GetRawText(), cloudJson)
                    .BuildUniformBuffer(0, 0, sizeof(float) * 20, SharingMode.Exclusive)
                    .BuildGraphicsPipelineFromJson(mainGroup.RenderPass, pipelineCache, cloudInfos, cloudPipelineJson);
                var cloud = graphicsManager.CreateSecondaryVulkanRenderGroup("CloudGroup", cloudPipeline)
                    .Binding(secondary);
                cloud.SetCamera(camera);
                cloud.CreateGroupRenderingAction((builder) => builder.UseMiddleware<CloudRenderingMiddleware>());
                cloud.CreateItemRenderingAction((builder) => builder.UseMiddleware<BallItemMiddleware>());
            }
            catch (Exception e)
            {
                _Logger.LogError(e.Message + "\r\n" + e.StackTrace);
            }
        }

        protected override Task Unload(VulkanGraphicsManager manager)
        {
            return Task.CompletedTask;
        }
    }
}
